热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

小时|个位数_ESP8266便携式物联网时钟(软件篇)代号:喵

篇首语:本文由编程笔记#小编为大家整理,主要介绍了ESP8266便携式物联网时钟(软件篇)代号:喵相关的知识,希望对你有一定的参考价值。接上一篇的硬件,本

篇首语:本文由编程笔记#小编为大家整理,主要介绍了ESP8266便携式物联网时钟(软件篇) 代号:喵相关的知识,希望对你有一定的参考价值。


接上一篇的硬件,本篇说说开发环境和作品功能的实现及部分核心代码



    • 1.开发环境 - Arduino IDE for ESP8266
    • 2.NTP网络时间获取
    • 3.32x8 LED点阵显示
    • 4.RTC时钟更新写入
    • 5. 初始化函数整合
    • 6. 手势识别
    • 7.点阵显示
    • 8.手势切换显示
    • 9.物联网时钟外壳





1.开发环境 - Arduino IDE for ESP8266

使用Arduino开发平台来开发ESP8266,可以延用Arduino的变成语言,便捷高效,就是安装环境和编译代码的时候稍微费点时间,环境安装参考链接
Arduino IDE – ESP8266开发环境搭建
当然,也不是所有人都能一次性安装成功的,这里提供安装失败的参考方法
ESP8266 – Arduino IDE开发环境配置失败解决方式参考

成功搭建开发环境后,在IDE开发板选项中可以找到ESP8266系列的开发板,如果你用的板子是ESP-12F,那么就选如图的开发板就行,其他参数默认即可


2.NTP网络时间获取

对于物联网时钟来说,联网和实时时间获取都是必要且基础的
ESP8266网络连接,核心代码如下,是否联网成功可以从 WiFi.status() 的返回值得知,具体可以参考文章 ESP8266(ESP-12F) 学习笔记1 – 网络连接

char *ssid = "WiFi_name"; //路由器/热点名称
char *pass = "123456789"; //路由器/热点密码
WiFi.begin(ssid,pass); //ESP8266连接外部路由器/热点

同步NTP时间,在Arduino IDE中需安装NTP库,库示例代码如下,串口持续打印 .... 直到网络连接成功,loop函数中每秒进行时间更新和打印当前的 时-分-秒

#include
// change next line to use with another board/shield
#include
//#include // for WiFi shield
//#include // for WiFi 101 shield or MKR1000
#include
const char *ssid = "";
const char *password = "";
WiFiUDP ntpUDP;
NTPClient timeClient(ntpUDP);
void setup()
Serial.begin(115200);
WiFi.begin(ssid, password);
while ( WiFi.status() != WL_CONNECTED )
delay ( 500 );
Serial.print ( "." );

timeClient.begin();

void loop()
timeClient.update();
Serial.println(timeClient.getFormattedTime());
delay(1000);

需要注意的地方,构造函数timeClient可传参连接的NTP服务器,以及获取时间的时区(重点),不指定时区的话默认是东一区的时间,北京时间是东八区,服务器不指定的话默认是 pool.ntp.org

WiFiUDP ntp_udp;
timeClient(ntp_udp,"ntp1.aliyun.com",60*60*8,60000);

也可以调用 setTimeOffset 函数设置时区

timeClient.setTimeOffset(60*60*8); //东八区时间设置

时间获取相关函数,周几(返回值0-6,星期天是0),小时,分钟,秒数

int getDay() const;
int getHours() const;
int getMinutes() const;
int getSeconds() const;

详细参考文章链接 ESP8266(ESP12F)学习笔记2 – NTP网络时间获取


3.32x8 LED点阵显示

显示主体定制的PCB点阵,在代码中需要做LED点阵初始化和各种显示取模
软件篇中提到的某平台大佬做的物联网时钟,分享的代码部分相关显示的库已经在Arduino上找不到支持库了,所以NTP和点阵显示都需要重新找支持库(点阵屏用 LedControl 库)
LedControl库使用可参考链接 [ESP8266(ESP-12F) 第三方库使用 – (https://blog.csdn.net/qq_36955622/article/details/120077367)

#include
// CONNECTIONS:
// 32*8 LED Display DIN --> ESP-12F D7
// 32*8 LED Display CS --> ESP-12F D6
// 32*8 LED Display CLK --> ESP-12F D5
int DIN = D7;
int CS = D6;
int CLK = D5;
//LED Display DEFINE
LedControl DC = LedControl(DIN,CLK,CS,4);
// 点阵初始化
void LEDInit()

//初始化address 0-3的8*8点阵屏
for(int i &#61; 0;i<4;i&#43;&#43;)

DC.shutdown(i,false); //启动时&#xff0c;MAX72XX处于省电模式
DC.setIntensity(i,12); //亮度设置
DC.clearDisplay(i); //清除显示


至于点阵要显示的内容就要靠取模软件来生成字库了&#xff0c;将生成的16进制字库保存在代码数组中
在上电之后进行联网的同时&#xff0c;可以在点阵屏做点表情互动来表示联网状态&#xff0c;联网中就眨巴眼睛&#xff0c;联网成功了或者限定时间内未成功联网则表情显示为球形眼珠的

眨眼表情的代码放在联网检测代码中

while(WiFi.status() !&#61; WL_CONNECTED)

// 联网过程中点阵显示互动表情
...

// 切换表情表示已联网成功或超时
...

点阵显示可以参考链接 ESP8266&#xff08;ESP-12F&#xff09;案例实操 – 8x32点阵显示&#xff08;MAX7219&#xff09;


4.RTC时钟更新写入

RTC时钟使用到DS1302时钟模块&#xff0c;跟点阵一样&#xff0c;都需要3个IO&#xff08;DAT/CLK/RST&#xff09;来写入数据和读出数据&#xff0c;用到 RtcDS1302

#include
//RTC DS1302 CONNECTIONS:
// DS1302 CLK/SCLK --> ESP-12F D2
// DS1302 DAT/IO --> ESP-12F D1
// DS1302 RST/CE --> ESP-12F D0
int DS1302_RST &#61; D0;
int DS1302_DAT &#61; D1;
int DS1302_CLK &#61; D2;
//DS1302 DEFINE
ThreeWire DS1302Wire(DS1302_DAT,DS1302_CLK,DS1302_RST); // DAT, CLK, RST
RtcDS1302<ThreeWire> Rtc(DS1302Wire);

上电时&#xff0c;在setup中先更新DS1302的内置时间&#xff08;NTP获取的时间&#xff09;&#xff0c;可以直接调用函数写入

if(timeClient.update())
// 将时间写入DS_time_update&#xff0c;包括年月日&#xff08;DS1302内置&#xff09;&#xff0c;时分秒&#xff08;直接那NTP服务器的时间&#xff09;
RtcDateTime DS_time_update(DS_time.Year(),DS_time.Month(),DS_time.Day(),timeClient.getHours(),
timeClient.getMinutes(),timeClient.getSeconds());
Rtc.SetDateTime(DS_time_update); // 写入时间到DS1302
Rtc.SetIsWriteProtected(false); // 关闭写保护
Rtc.SetIsRunning(true); // 运行DS1302
Serial.println("RTC update Success !!");

DS1302模块的内置日期不一定是对的&#xff0c;偶尔断电或者电流不够&#xff0c;重新上电时&#xff0c;就会变成2000年1月1日&#xff0c;第一次写入日期可以是下面这样&#xff0c;然后再更新一次上面的代码进去&#xff0c;日期会随着时间去更新

if(timeClient.update())
// 将时间写入DS_time_update&#xff0c;包括年月日&#xff08;手动写入&#xff09;&#xff0c;时分秒&#xff08;直接那NTP服务器的时间&#xff09;
RtcDateTime DS_time_update(2021,11,24,timeClient.getHours(),
timeClient.getMinutes(),timeClient.getSeconds());
Rtc.SetDateTime(DS_time_update); // 写入时间到DS1302
Rtc.SetIsWriteProtected(false); // 关闭写保护
Rtc.SetIsRunning(true); // 运行DS1302
Serial.println("RTC update Success !!");


5. 初始化函数整合

setup函数初始化上面列出来的几个部分&#xff0c;大致如下&#xff08;不确定完整性&#xff09;&#xff0c;注意需要添加看门狗&#xff0c;防程序跑死

// NTP时间获取
#include
#include
#include
// DS1302模块
#include
#include
// 8*&#xff08;8&#43;24&#xff09;LED点阵显示
#include
//RTC DS1302 CONNECTIONS:
// DS1302 CLK/SCLK --> ESP-12F D2
// DS1302 DAT/IO --> ESP-12F D1
// DS1302 RST/CE --> ESP-12F D0
int DS1302_RST &#61; D0;
int DS1302_DAT &#61; D1;
int DS1302_CLK &#61; D2;
// CONNECTIONS:
// 32*8 LED Display DIN --> ESP-12F D7
// 32*8 LED Display CS --> ESP-12F D6
// 32*8 LED Display CLK --> ESP-12F D5
int DIN &#61; D7;
int CS &#61; D6;
int CLK &#61; D5;
//LED Display DEFINE
LedControl DC &#61; LedControl(DIN,CLK,CS,4);
//NTP DEFINE
WiFiUDP ntp_udp;
NTPClient timeClient(ntp_udp,"ntp7.aliyun.com",60*60*8,60000); //定位为东八区的时间
//DS1302 DEFINE
ThreeWire DS1302Wire(DS1302_DAT,DS1302_CLK,DS1302_RST); // DAT, CLK, RST
RtcDS1302<ThreeWire> Rtc(DS1302Wire);
//更新DS1302时间&#xff08;联网状态下&#xff09;
void RTC_ClockUpdate()

if(timeClient.update())
// 将时间写入DS_time_update&#xff0c;包括年月日&#xff08;DS1302内置&#xff09;&#xff0c;时分秒&#xff08;直接那NTP服务器的时间&#xff09;
RtcDateTime DS_time_update(DS_time.Year(),DS_time.Month(),DS_time.Day(),timeClient.getHours(),
timeClient.getMinutes(),timeClient.getSeconds());
Rtc.SetDateTime(DS_time_update); // 写入时间到DS1302
Rtc.SetIsWriteProtected(false); // 关闭写保护
Rtc.SetIsRunning(true); // 运行DS1302
Serial.println("RTC update Success !!");


void setup()
LEDInit(); // LED点阵初始化
Serial.begin(115200);
WiFi.begin(SSID,PASS); // 连接WiFi
uint8_t connect_time &#61; 0; // 循环眨眼次数
// 联网检测&#xff0c;联网成功可在超时前退出while
while(WiFi.status() !&#61; WL_CONNECTED)

// 循环眨眼
for(int i &#61; 0;i<2;i&#43;&#43;)

// 眨眼代码
...

// 眨眼1次&#xff0c;变量自加
connect_time&#43;&#43;;
// 超时眨眼次数 累计4次后未联网直接跳出while
if(connect_time &#61;&#61; 4) break;

//显示眼球图案(联网/超时)
CatDisplay();
delay(750);
Rtc.Begin(); //初始化RTC - DS1302模块
timeClient.begin(); //初始化NTP服务
RTC_ClockUpdate(); //更新DS1302时间&#xff08;联网状态下&#xff09;

ESP.wdtEnable(5000); //开启看门狗&#xff0c;需要5秒喂狗一次

void loop()
ESP.wdtFeed(); // 喂狗


6. 手势识别

安装手势识别相关库 SparkFun_APDS9960&#xff0c;手势识别模块型号 APDS9960

#include
#include
// Pins
#define APDS9960_INT 2 // 需要一个中断引脚
// Global Variables
SparkFun_APDS9960 apds &#61; SparkFun_APDS9960();
int isr_flag &#61; 0;
void setup()
// 设置中断引脚为输入模式
pinMode(APDS9960_INT, INPUT);
// 初始化串口
Serial.begin(115200);
Serial.println();
Serial.println(F("--------------------------------"));
Serial.println(F("SparkFun APDS-9960 - GestureTest"));
Serial.println(F("--------------------------------"));

// 初始化外部中断
attachInterrupt(0, interruptRoutine, FALLING);
// 初始化APDS9960模块&#xff08;I2C&#xff09;
if ( apds.init() )
Serial.println(F("APDS-9960 initialization complete"));
else
Serial.println(F("Something went wrong during APDS-9960 init!"));


// 开始运行模块
if ( apds.enableGestureSensor(true) )
Serial.println(F("Gesture sensor is now running"));
else
Serial.println(F("Something went wrong during gesture sensor init!"));


void loop()
if( isr_flag &#61;&#61; 1 )
detachInterrupt(0);
handleGesture();
isr_flag &#61; 0;
attachInterrupt(0, interruptRoutine, FALLING);


void interruptRoutine()
isr_flag &#61; 1;

// 处理手势&#xff0c;将对应手势通过串口打印出来
void handleGesture()
if ( apds.isGestureAvailable() )
switch ( apds.readGesture() )
case DIR_UP:
Serial.print("UP");
break;
case DIR_DOWN:
Serial.print("DOWN");
break;
case DIR_LEFT:
Serial.print("LEFT");
break;
case DIR_RIGHT:
Serial.print("RIGHT");
break;
case DIR_NEAR:
Serial.print("NEAR");
break;
case DIR_FAR:
Serial.print("FAR");
break;
default:
Serial.print("NONE");





该手势识别库不兼容ESP8266&#xff0c;需要用手势识别时要另外用一块Arduino控制板与ESP8266做串口交互



7.点阵显示

除了上面说到的联网时的造型显示外&#xff0c;点阵还需要显示当前的时间

定义时间段&#xff0c;在顶部的8x8点阵显示时间状态&#xff08;白天或晚上&#xff09;&#xff0c;凌晨6点至晚上18点定义为白天&#xff0c;18点至凌晨6点定义为晚上&#xff0c;点阵根据时间段显示太阳图案和月亮图案&#xff0c;如上图为月亮图案

#define IS_DAY (6 <&#61; DS_time.Hour()) && (DS_time.Hour() < 18)
#define IS_NIGHT (DS_time.Hour() < 6) || (DS_time.Hour() >&#61; 18)

Arduino的程序都是以单线程的形式在运行&#xff0c;要执行类似多线程可以比较实时的运行多个任务很难实现&#xff0c;因此有个比较巧妙的方法可以做到&#xff0c;利用 millis 函数可以比较ok的完成类似多个实时任务的执行&#xff0c;原理是 millis - 最后一次任务运行记录的时间 >&#61; 定时执行该任务的时间 但最好也别超过5个

void loop()
ESP.wdtFeed(); //循环喂狗
// 每秒执行一次&#xff0c;UPUATE_TIME &#61; 1000毫秒
if((millis() - loop_times) >&#61; UPUATE_TIME)
Time_DisplayUpdate(); // 检测0-2点阵上的时间是否需要刷新&#xff08;小时-分钟&#xff09;
DayorNight_Update(); // 根据时间段判断3号点阵该显示太阳或月亮

RTC_DATETIME_GET; // 读取DS1302的时间
// 判断DS1302读取的秒数&#xff0c;奇数和偶数做不同显示&#xff08;小时与分钟显示之间冒号的亮灭&#xff09;
if((time.Second()%2)&#61;&#61;0) DC.setColumn(1,3,st_display[0]);
else DC.setColumn(1,3,st_undisplay[0]);
// 记录该次循环的时间
loop_times &#61; millis();


下面部分时间刷新代码&#xff0c;num_display 是0-9的数字取模&#xff0c;各占3列点阵

// 如果小时和分钟只有个位数的数值需要更新&#xff0c;那么只刷新LED点阵的个位数显示&#xff0c;只在时间有变化时才做刷新
if(last_DS_minutes !&#61; DS_minute)

if(DS_minute >&#61; 10)

if((DS_minute/10) &#61;&#61; (last_DS_minutes/10))

Display_3col(num_display,2,DS_minute%10,1);
else
Display_3col(num_display,1,DS_minute/10,5);
Display_3col(num_display,2,DS_minute%10,1);

else
if(DS_minute < 10)

if(DS_minute &#61;&#61; 0)

Display_3col(num_display,1,0,5);
Display_3col(num_display,2,0,1);
else
Display_3col(num_display,1,0,5);
Display_3col(num_display,2,DS_minute,1);


last_DS_minutes &#61; DS_minute;

显示函数是包装了LedControl库的函数&#xff0c;传参包括点阵设备号&#xff0c;要显示的二维数组行数&#xff0c;及偏移位

// display three col
void Display_3col(byte character[][3],int device_num,int col,int point)

for(int i &#61; 0;i&l

推荐阅读
  • 本文介绍了在开发Android新闻App时,搭建本地服务器的步骤。通过使用XAMPP软件,可以一键式搭建起开发环境,包括Apache、MySQL、PHP、PERL。在本地服务器上新建数据库和表,并设置相应的属性。最后,给出了创建new表的SQL语句。这个教程适合初学者参考。 ... [详细]
  • 本文介绍了九度OnlineJudge中的1002题目“Grading”的解决方法。该题目要求设计一个公平的评分过程,将每个考题分配给3个独立的专家,如果他们的评分不一致,则需要请一位裁判做出最终决定。文章详细描述了评分规则,并给出了解决该问题的程序。 ... [详细]
  • 本文主要解析了Open judge C16H问题中涉及到的Magical Balls的快速幂和逆元算法,并给出了问题的解析和解决方法。详细介绍了问题的背景和规则,并给出了相应的算法解析和实现步骤。通过本文的解析,读者可以更好地理解和解决Open judge C16H问题中的Magical Balls部分。 ... [详细]
  • 如何用UE4制作2D游戏文档——计算篇
    篇首语:本文由编程笔记#小编为大家整理,主要介绍了如何用UE4制作2D游戏文档——计算篇相关的知识,希望对你有一定的参考价值。 ... [详细]
  • 本文介绍了一种划分和计数油田地块的方法。根据给定的条件,通过遍历和DFS算法,将符合条件的地块标记为不符合条件的地块,并进行计数。同时,还介绍了如何判断点是否在给定范围内的方法。 ... [详细]
  • 本文介绍了P1651题目的描述和要求,以及计算能搭建的塔的最大高度的方法。通过动态规划和状压技术,将问题转化为求解差值的问题,并定义了相应的状态。最终得出了计算最大高度的解法。 ... [详细]
  • Java String与StringBuffer的区别及其应用场景
    本文主要介绍了Java中String和StringBuffer的区别,String是不可变的,而StringBuffer是可变的。StringBuffer在进行字符串处理时不生成新的对象,内存使用上要优于String类。因此,在需要频繁对字符串进行修改的情况下,使用StringBuffer更加适合。同时,文章还介绍了String和StringBuffer的应用场景。 ... [详细]
  • 本文介绍了解决二叉树层序创建问题的方法。通过使用队列结构体和二叉树结构体,实现了入队和出队操作,并提供了判断队列是否为空的函数。详细介绍了解决该问题的步骤和流程。 ... [详细]
  • 本文介绍了C函数ispunct()的用法及示例代码。ispunct()函数用于检查传递的字符是否是标点符号,如果是标点符号则返回非零值,否则返回零。示例代码演示了如何使用ispunct()函数来判断字符是否为标点符号。 ... [详细]
  • 动态规划算法的基本步骤及最长递增子序列问题详解
    本文详细介绍了动态规划算法的基本步骤,包括划分阶段、选择状态、决策和状态转移方程,并以最长递增子序列问题为例进行了详细解析。动态规划算法的有效性依赖于问题本身所具有的最优子结构性质和子问题重叠性质。通过将子问题的解保存在一个表中,在以后尽可能多地利用这些子问题的解,从而提高算法的效率。 ... [详细]
  • CF:3D City Model(小思维)问题解析和代码实现
    本文通过解析CF:3D City Model问题,介绍了问题的背景和要求,并给出了相应的代码实现。该问题涉及到在一个矩形的网格上建造城市的情景,每个网格单元可以作为建筑的基础,建筑由多个立方体叠加而成。文章详细讲解了问题的解决思路,并给出了相应的代码实现供读者参考。 ... [详细]
  • 微软头条实习生分享深度学习自学指南
    本文介绍了一位微软头条实习生自学深度学习的经验分享,包括学习资源推荐、重要基础知识的学习要点等。作者强调了学好Python和数学基础的重要性,并提供了一些建议。 ... [详细]
  • 本文介绍了RPC框架Thrift的安装环境变量配置与第一个实例,讲解了RPC的概念以及如何解决跨语言、c++客户端、web服务端、远程调用等需求。Thrift开发方便上手快,性能和稳定性也不错,适合初学者学习和使用。 ... [详细]
  • 本文介绍了计算机网络的定义和通信流程,包括客户端编译文件、二进制转换、三层路由设备等。同时,还介绍了计算机网络中常用的关键词,如MAC地址和IP地址。 ... [详细]
  • Android源码深入理解JNI技术的概述和应用
    本文介绍了Android源码中的JNI技术,包括概述和应用。JNI是Java Native Interface的缩写,是一种技术,可以实现Java程序调用Native语言写的函数,以及Native程序调用Java层的函数。在Android平台上,JNI充当了连接Java世界和Native世界的桥梁。本文通过分析Android源码中的相关文件和位置,深入探讨了JNI技术在Android开发中的重要性和应用场景。 ... [详细]
author-avatar
Scarlett_girl
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有